home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / os2 / pvm34b3.zip / pvm34b3 / pvm3 / examples / mhf_tickle.c < prev    next >
C/C++ Source or Header  |  1997-07-22  |  19KB  |  641 lines

  1.  
  2. static char rcsid[] =
  3.     "$Id: mhf_tickle.c,v 1.3 1997/07/09 13:25:13 pvmsrc Exp $";
  4.  
  5. /*
  6.  *         PVM version 3.4:  Parallel Virtual Machine System
  7.  *               University of Tennessee, Knoxville TN.
  8.  *           Oak Ridge National Laboratory, Oak Ridge TN.
  9.  *                   Emory University, Atlanta GA.
  10.  *      Authors:  J. J. Dongarra, G. E. Fagg, M. Fischer
  11.  *          G. A. Geist, J. A. Kohl, R. J. Manchek, P. Mucci,
  12.  *         P. M. Papadopoulos, S. L. Scott, and V. S. Sunderam
  13.  *                   (C) 1997 All Rights Reserved
  14.  *
  15.  *                              NOTICE
  16.  *
  17.  * Permission to use, copy, modify, and distribute this software and
  18.  * its documentation for any purpose and without fee is hereby granted
  19.  * provided that the above copyright notice appear in all copies and
  20.  * that both the copyright notice and this permission notice appear in
  21.  * supporting documentation.
  22.  *
  23.  * Neither the Institutions (Emory University, Oak Ridge National
  24.  * Laboratory, and University of Tennessee) nor the Authors make any
  25.  * representations about the suitability of this software for any
  26.  * purpose.  This software is provided ``as is'' without express or
  27.  * implied warranty.
  28.  *
  29.  * PVM version 3 was funded in part by the U.S. Department of Energy,
  30.  * the National Science Foundation and the State of Tennessee.
  31.  */
  32.  
  33. /***************************************************************
  34. *    Filename: mhf_tickle.c
  35. *
  36. *  This program "tickles" the mhf_server program for testing
  37. *  message handler functionality.  Also serves as an example
  38. *  of how you may use and drive the message handlers...
  39. *
  40. *  Operation:
  41. *    1. startup pvm console
  42. *    2. startup the mhf_server
  43. *    3. startup as many mhf_tickle instances as you wish
  44. *        - follow menu options and in-code comments on
  45. *          how to run and what it tests / shows...
  46. *
  47. *  This code is dedicated to "Tickle-Me-Elmore".
  48. *  And yes that is Elmore....
  49. ***************************************************************/
  50.  
  51. #include "stdio.h"
  52. #ifndef WIN32
  53. #include <sys/errno.h>
  54. #endif
  55. #include "pvm3.h"
  56.  
  57. /* #define NOISE1 1     sets level of program noise  - minimum noise */
  58.  
  59. #define  PMH 0        /* display present message handlers        */
  60. #define  LMH 1        /* load message handler                */
  61. #define  DMH 2        /* delete message handler            */
  62. #define  ERJ 3        /* tell mhf_server() to pvm_exit( ) and then rejoin. */
  63. #define  HIT 4        /* message to hit a specified handler...    */
  64. #define GSMB 7        /* use to grab the mhf_server contact info from mailbox again... */
  65. #define CEAT 8        /* client ( this program ) exit pvm and terminate self  - server not contacted... */
  66. #define SEAT 9        /* server exits pvm and terminate self        */
  67.  
  68. #define SC1 1        /* string_catcher1 */
  69. #define SC2 2        /* string_catcher2 */
  70. #define SC3 3        /* string_catcher3 */
  71. #define SC4 4        /* string_catcher4 */
  72. #define SC5 5        /* string_catcher5 */
  73. #define IC1 6        /* int_catcher6 */
  74. #define IC2 7        /* int_catcher7 */
  75. #define IC3 8        /* int_catcher8 */
  76. #define IC4 9        /* int_catcher9 */
  77. #define IC5 0        /* int_catcher10 */
  78.  
  79. #define STR_LEN 100
  80. #define TRUE 1
  81. #define FALSE 0
  82. #define YES TRUE
  83. #define NO FALSE
  84. #define SUCCESS 0
  85. #define FAILED -1
  86.  
  87. extern int errno;
  88.  
  89. /*
  90. *  private - function prototypes
  91. */
  92. int locate_server( char *, int *, int *, char * );
  93. int mhl( );
  94. int mhto( );
  95. int getx_line( int * );
  96. int geti_line( int * );
  97. char *gets_lim_nw( int, char * );
  98. char getc_line( char * );
  99. void eat_line( char c );
  100. void press_enter_key( char * );
  101. char *gets_lim_ows( int, char * );
  102.  
  103.  
  104. /***************************************************************/
  105. main ( argc, argv ) 
  106. int argc;
  107. char *argv[];
  108. {    char *me = "mhf_tickle";
  109.     char machine[25];
  110.     int mytid;
  111.     int ctx;             /* client original context */
  112.     int i;
  113.     char msg_txt[STR_LEN];    /* used for sending messages */
  114.  
  115.     int stid;        /* server's tid */
  116.     int sctx;        /* server's context */
  117.     char *service = "mhf_server";
  118.     char server[25];    /* machine name of server */
  119.  
  120.     int mh_src;        /* message handler source ( tid ) to talk to */
  121.     int mh_tag;        /* message handler tag to talk to */
  122.     int mh_ctx;        /* message handler context to talk to */
  123.     int mhid;        /* message handler id - used to delete them */
  124.  
  125.     int option;        /* user entered option selection */
  126.     int load_mh;        /* message handler to load */
  127.  
  128.     /*
  129.     *  for display - note IC5 position
  130.     */
  131.     char *mh_arr[] = { "IC5", "SC1", "SC2", "SC3", "SC4", "SC5", "IC1", "IC2", "IC3", "IC4" };
  132.  
  133.     mytid = pvm_mytid( );
  134.     ctx = pvm_getcontext( );            /* original starting context */
  135.     gethostname( machine, 25 );
  136. #ifdef NOISE1
  137.     printf( "%s: t%x on machine <%s> with context %d.\n", me, mytid, machine, ctx );
  138. #endif
  139.  
  140.     if ( locate_server( service, &stid, &sctx, server ) < 0 ){
  141.         pvm_exit( );
  142.         exit( -1 );
  143.     }
  144. #ifdef NOISE1
  145.     printf( "%s: just retrieved mailbox with stid = t%x   sctx = %d   server = %s   service = %s.\n", me, stid, sctx, server, service );
  146. #endif
  147.  
  148.     do{
  149.  
  150.     option = mhto( );    /* get Message Handler Trial Options */
  151.  
  152.     switch ( option ){
  153.     case PMH:/*****
  154.         *  display present message handlers
  155.         */
  156.         printf( "\n%s: PMH - display present message handlers\n", me );
  157.         pvm_setcontext( sctx );        /* set my context to that of server */
  158.         sprintf( msg_txt, "Are you talking to me!!!" );
  159.         pvm_initsend( PvmDataDefault );
  160.         pvm_pkstr( msg_txt );
  161.         pvm_send( stid, PMH );
  162.         printf( "%s: sent--<%s> to:  stid = t%x   sctx = %d.\n", me, msg_txt, stid, sctx );fflush( stdout );
  163.         break;
  164.  
  165.     case LMH:/*****
  166.         *  load message handler, ( src=tid_of_sender, tag, ctx )
  167.         */
  168.         printf( "\n%s: client:  src=tid = t%x  ctx = %d.", me, mytid, ctx );
  169.         printf( "\n%s: server:  src=tid = t%x  ctx = %d.\n", me, stid, sctx );
  170.         printf( "\n%s: LMH - load message handler", me );
  171.         pvm_setcontext( sctx );        /* set my context to that of server */
  172.         pvm_initsend( PvmDataDefault );
  173.  
  174.         mh_src = mytid;        /* can't spoof a source tid - must use existing one... */
  175.         printf( "\n%s: message source tid: t%x\n", me, mh_src );
  176.  
  177.         pvm_pkint( &mh_src, 1, 1 );
  178.  
  179.         printf( "%s: message source tag: ", me );
  180.         if ( geti_line( &mh_tag ) != SUCCESS ){
  181.             printf( "\n%s: problem on source tag - try again...\n", me );
  182.             break;
  183.         }
  184.         pvm_pkint( &mh_tag, 1, 1 );
  185.  
  186.         printf( "%s: message source ctx: ", me );
  187.         if ( geti_line( &mh_ctx ) != SUCCESS ){
  188.             printf( "\n%s: problem on context input - try again...\n", me );
  189.             break;
  190.         }
  191.         pvm_pkint( &mh_ctx, 1, 1 );
  192.  
  193.         load_mh = mhl( );
  194.  
  195.         pvm_pkint( &load_mh, 1, 1 );    /* handler to load in mhf_server( ) code */
  196.         printf( "\n%s: will attempt loading message handler %s with src = t%x = %d,  tag = %d, ctx = %d\n", 
  197.             me, mh_arr[load_mh], mh_src, mh_src, mh_tag, mh_ctx );
  198.         pvm_send( stid, LMH );
  199.         break;
  200.  
  201.     case DMH:/*****
  202.         *  delete message handler
  203.         */
  204.         printf( "\n%s: DMH - delete message handler", me );
  205.         pvm_setcontext( sctx );        /* set my context to that of server */
  206.         printf( "\n%s: delete message handler id: ", me );
  207.         if ( geti_line( &mhid ) != SUCCESS ){
  208.             printf( "\n%s: problem on message handler id input - try again...\n", me );
  209.             break;
  210.         }
  211.         pvm_initsend( PvmDataDefault );
  212.         pvm_pkint( &mhid, 1, 1 );
  213.         pvm_send( stid, DMH );
  214.         break;
  215.  
  216.     case ERJ:/*****
  217.         *  tell mhf_server() to pvm_exit( ) and then rejoin. ->> check message handlers existance
  218.         */
  219.         printf( "\n%s: ERJ - tell mhf_server() to pvm_exit( )", me );
  220.         pvm_setcontext( sctx );        /* set my context to that of server */
  221.         pvm_initsend( PvmDataDefault );
  222.         pvm_send( stid, ERJ );
  223.         /*
  224.         *  must relocate the server as past info is now stale
  225.         */
  226.         printf( "%s: Looking for service = %s.\n", me, service );
  227.         press_enter_key( "Press Enter key when server is ready to continue..." );
  228.         if ( locate_server( service, &stid, &sctx, server ) < 0 ){
  229.             pvm_exit( );
  230.             exit( -1 );
  231.         }
  232.         printf( "%s: just retrieved mailbox with stid = t%x   sctx = %d   server = %s   service = %s.\n",
  233.             me, stid, sctx, server, service );
  234.         break;
  235.  
  236.     case HIT:/*****
  237.         * message to hit a specified handler...
  238.         */
  239.         printf( "\n%s: HIT - message to hit a specified handler...", me );
  240.  
  241.         printf( "\n%s: set message tag: ", me );
  242.         if ( geti_line( &mh_tag ) != SUCCESS ){
  243.             printf( "\n%s: problem on source tag - try again...\n", me );
  244.             break;
  245.         }
  246.  
  247.         printf( "%s: message source ctx: ", me );
  248.         if ( geti_line( &mh_ctx ) != SUCCESS ){
  249.             printf( "\n%s: problem on context input - try again...\n", me );
  250.             break;
  251.         }
  252.  
  253.         pvm_setcontext( mh_ctx );        /* set my context to that which handler expects */
  254.  
  255.         load_mh = mhl( );        /* select handler to "hit" */
  256.         pvm_initsend( PvmDataDefault );
  257.         pvm_pkint( &load_mh, 1, 1 );    /* integer indicates which trying to ping */
  258.         switch ( load_mh ) {
  259.             case 1: case 2: case 3: case 4: case 5:
  260.                 printf( "\n%s: Enter message to ping message handler string_catcher%d: ",me, load_mh );
  261.                 gets_lim_ows( STR_LEN, msg_txt );
  262.                 printf( "\n%s: will ping %s:string_catcher%d with message <%s>.\n", me, mh_arr[load_mh], load_mh, msg_txt );
  263.                 pvm_pkstr( msg_txt );
  264.                 break;
  265.  
  266.             case 0: case 6: case 7: case 8: case 9:
  267.                 printf( "\n%s: Enter integer to ping message handler int_catcher%d: ",me, load_mh );
  268.                 geti_line( &i );
  269.                 printf( "\n%s: will ping %s:int_catcher%d with integer <%d>.\n", me, mh_arr[load_mh], load_mh, i );
  270.                 pvm_pkint( &i, 1, 1 );
  271.                 break;
  272.  
  273.             default: /* should never get here... */
  274.                 printf( "\n\n%s: mhl( ) returned value <%d> out of bounds...\n\n", me, load_mh );
  275.                 pvm_exit( );
  276.                 exit( -1 );
  277.                 break;
  278.         }
  279.         pvm_send( stid, mh_tag );        /* the send that the mhf should intercept */
  280.  
  281.         /*
  282.         *  now tickle the mhf_server so that it knows that a 
  283.         *  message should have been intercepted on its behalf
  284.         */
  285.         pvm_setcontext( sctx );        /* set my context to that of server */
  286.         pvm_initsend( PvmDataDefault );
  287.         pvm_pkint( &load_mh, 1, 1 );    /* integer indicates which trying to ping */
  288.         switch ( load_mh ) {
  289.             case 1: case 2: case 3: case 4: case 5:
  290.                 pvm_pkstr( msg_txt );
  291.                 break;
  292.             case 0: case 6: case 7: case 8: case 9:
  293.                 pvm_pkint( &i, 1, 1 );
  294.                 break;
  295.         }
  296.         pvm_send( stid, HIT );            /* the send to "tickle" mhf_server with */
  297.         break;
  298.  
  299.     case GSMB:/*****
  300.         *  use to grab the mhf_server contact info from mailbox again...
  301.         *  No actual contact with mhf_server - only its entry into mailbox
  302.         *
  303.         *  Necessary if another instance of mhf_tickle does a ERJ on you...
  304.         */
  305.         if ( locate_server( service, &stid, &sctx, server ) < 0 ){
  306.             pvm_exit( );
  307.             exit( -1 );
  308.         }
  309. #ifdef NOISE1
  310.         printf( "%s: just retrieved mailbox with stid = t%x   sctx = %d   server = %s   service = %s.\n", me, stid, sctx, server, service );
  311. #endif
  312.         break;
  313.  
  314.     case SEAT:/*****
  315.         *  server exits pvm and terminate self
  316.         */
  317.         printf( "\n%s: SEAT - server exits pvm and terminate self\n\n", me );
  318.         pvm_setcontext( sctx );        /* set my context to that of server */
  319.         pvm_initsend( PvmDataDefault );
  320.         pvm_send( stid, SEAT );
  321.         pvm_exit( );
  322.         exit( 0 );
  323.         break;
  324.  
  325.     case CEAT:/*****
  326.         *  client ( this program ) exits pvm and terminate self - server not contacted...
  327.         */
  328.         printf( "\n%s: CEAT - client ( this program ) exits pvm and terminate self\n\n", me );
  329.         pvm_exit( );
  330.         exit( 0 );
  331.         break;
  332.  
  333.     default:/*******
  334.         *  invalid msgtag - just ignore the message
  335.         */
  336.         printf( "\n%s: default - junk input - try again...", me );
  337.             break;
  338.     }
  339.     pvm_setcontext( ctx );        /* reset context to that of client program mhf_tickle( ) */
  340.     } while ( ( option != SEAT ) && ( option != CEAT ) );
  341.  
  342.  
  343.  
  344.     pvm_exit( );
  345.     exit( 0 );
  346. } /* end_main */
  347.  
  348.  
  349. /***************************************************************/
  350.  
  351.  
  352.  
  353. /*
  354. *  look for the user specified service name
  355. */
  356. int
  357. locate_server( service, tid, ctx, server )
  358. char *service;
  359. int *tid;
  360. int *ctx;
  361. char *server;
  362. {    char *me = "locate_server";
  363.     int msg_buf;
  364.  
  365.  
  366.     msg_buf = pvm_recvinfo( service, 0, PvmMboxDefault );
  367.     if ( msg_buf >= 0 ) {
  368.         pvm_setrbuf( msg_buf );
  369.         pvm_upkint( tid, 1, 1 );
  370.         pvm_upkint( ctx, 1, 1 );
  371.         pvm_upkstr( server );
  372.     }
  373.     else {
  374.         /*
  375.          *  problem retrieving mailbox entry - tell user why, exit pvm, and terminate task...
  376.          */
  377.         switch ( msg_buf ) {
  378.             case PvmExists:
  379.                 printf( "\n%s: Named service already running\n", me );
  380.                 break;
  381.             case PvmBadParam:
  382.                 printf( "\n%s: Invalid argument to pvm_recvinfo( ).\n", me );
  383.                 break;
  384.             case PvmNoSuchBuf:
  385.                 printf( "\n%s: Message buffer id does not exist.\n", me );
  386.                 break;
  387.             case PvmDenied:
  388.                 printf( "\n%s: Key locked by another task, can't delete.\n", me );
  389.                 break;
  390.             case PvmNoMem:
  391.                 printf( "\n%s: Libpvm is unable to allocate memory to pack data.\n", me );
  392.                 break;
  393.             case PvmNotFound:
  394.                 printf( "\n%s: Requested key does not exist.\n", me );
  395.                 break;
  396.             default:
  397.                 lpvmerr( "\nmhf_tickle: error %d", msg_buf );
  398.                 break;
  399.         } /* end_switch */
  400.         return( msg_buf );
  401.     }
  402. /*
  403.     printf( "%s: just retrieved mailbox with stid = t%x   sctx = %d   server = %s   service = %s.\n", 
  404.         me, *tid, *ctx, server, service );
  405. */
  406.     return( msg_buf );
  407. } /* locate_server */
  408.  
  409.  
  410.  
  411.  
  412. /*
  413. *  returns integer representing which message handler to load
  414. */
  415. int
  416. mhl( )
  417. {    char *me = "mhl";
  418.     char menu_selection;
  419.     int got_menu_selection = NO;
  420.     char zero = '0';
  421.  
  422.     do{
  423.         printf( "\n" );
  424.         printf( "\n   Load Message Handler" );
  425.         printf( "\n   --------------------" );
  426.         printf( "\n" );
  427.         printf( "\n     1. string_catcher1" );
  428.         printf( "\n     2. string_catcher2" );
  429.         printf( "\n     3. string_catcher3" );
  430.         printf( "\n     4. string_catcher4" );
  431.         printf( "\n     5. string_catcher5" );
  432.         printf( "\n     6. int_catcher6" );
  433.         printf( "\n     7. int_catcher7" );
  434.         printf( "\n     8. int_catcher8" );
  435.         printf( "\n     9. int_catcher9" );
  436.         printf( "\n     0. int_catcher10" );
  437.         printf( "\n" );
  438.         printf( "\n          Enter Selection: " );
  439.         getc_line( &menu_selection );
  440.         switch ( menu_selection ) {
  441.             case '0': /* drop through */
  442.             case '1': /* drop through */
  443.             case '2': /* drop through */
  444.             case '3': /* drop through */
  445.             case '4': /* drop through */
  446.             case '5': /* drop through */
  447.             case '6': /* drop through */
  448.             case '7': /* drop through */
  449.             case '8': /* drop through */
  450.             case '9': got_menu_selection = YES;
  451.         }
  452.     } while ( !got_menu_selection );
  453.     return ( menu_selection-zero );
  454. } /* mhl */
  455.  
  456.  
  457. /*
  458. *  returns integer value representing option selected ( not char value )
  459. */
  460. int
  461. mhto( )
  462. {    char *me = "mhto";
  463.     char menu_selection;
  464.     int got_menu_selection = NO;
  465.     char zero = '0';
  466.  
  467.     do{
  468.         printf( "\n" );
  469.         printf( "\n   Message Handler Trial Options" );
  470.         printf( "\n   -----------------------------" );
  471.         printf( "\n" );
  472.         printf( "\n     0. PMH - display present message handlers." );
  473.         printf( "\n     1. LMH - load message handler." );
  474.         printf( "\n     2. DMH - delete message handler." );
  475.         printf( "\n     3. ERJ - tell mhf_server() to pvm_exit( ) and then rejoin." );
  476.         printf( "\n     4. HIT - message to hit a specified handler..." );
  477.         printf( "\n     5.     - available." );
  478.         printf( "\n     6.     - available." );
  479.         printf( "\n     7. GSMB - get server from mail box again ( use if drop connection )." );
  480.         printf( "\n     8. CEAT - client ( this program ) exit pvm and terminate self." );
  481.         printf( "\n     9. SEAT - tell server to exit pvm and terminate self." );
  482.         printf( "\n" );
  483.         printf( "\n          Enter Selection: " );
  484.         getc_line( &menu_selection );
  485.         switch ( menu_selection ) {
  486.             case '0':    /* drop through */
  487.             case '1':    /* drop through */
  488.             case '2':    /* drop through */
  489.             case '3':    /* drop through */
  490.             case '4':    /* drop through */
  491.             case '7':    /* drop through */
  492.             case '8':    /* drop through */
  493.             case '9': got_menu_selection = YES;
  494.         }
  495.     } while ( !got_menu_selection );
  496.     return ( menu_selection-zero );
  497. } /* mhto */
  498.  
  499.  
  500. /*
  501. * get hex - move to next line
  502. */
  503. int 
  504. getx_line( i )
  505. int *i;
  506. {    char *me = "geti_line";
  507.     char c[STR_LEN];    /* temporary location to hold input string to convert to integer */
  508.  
  509.     gets_lim_nw( STR_LEN, c );    /* grab the input string */
  510.     *i =  strtol( c, ( char ** )NULL, 16 );    /* convert string to hexadecimal, bas 16 */
  511.     if ( errno == ERANGE ) return( FAILED );
  512.     return( SUCCESS );
  513. } /* getx_line */
  514.  
  515.  
  516. /*
  517. * get integer - move to next line
  518. */
  519. int
  520. geti_line( i )
  521. int *i;
  522. {    char *me = "geti_line";
  523.     char c[STR_LEN];    /* temporary location to hold input string to convert to integer */
  524.  
  525.     gets_lim_nw( STR_LEN, c );    /* grab the input string */
  526.     *i =  strtol( c, ( char ** )NULL, 10 );    /* convert string to integer, base 10 */
  527.     if ( errno == ERANGE ) return( FAILED );
  528.     return( SUCCESS );
  529. } /* geti_line */
  530.  
  531. /*
  532. * get string - no spaces
  533. */
  534. char *
  535. gets_lim_nw( max_len, s )
  536. int max_len;
  537. char *s;
  538. {    char *me = "gets_lim_nw";
  539.     char c;
  540.     int i = 0;
  541.     int first_char = TRUE;
  542.  
  543.     while ( ( ( c = getchar( ) ) != '\n' ) && ( i < ( max_len - 1 ) ) )
  544.     {
  545.         if ( !( isspace( c ) ) )            /* not a white space character */
  546.         {
  547.             /* printf( "\n%s: save char [%c] in position s[%d]", me, c, i );    sls-debug */
  548.             s[i++] = c;            /* not a white space, so keep the character */
  549.             first_char = FALSE;        /* mark having read the first non-white space char */
  550.         }
  551.         else                    /* is a white space character */
  552.         {
  553.             if ( !( first_char ) )    /* other than first character to read is whitespace */
  554.                 break;        /* kick out of the read loop */
  555.         }
  556.     }
  557.  
  558.     s[i] = NULL;
  559.  
  560.     eat_line( c );    /* eat the remaining characters on this line */
  561.  
  562.     return( s );
  563. } /* gets_lim_nw */
  564.  
  565.  
  566. /*
  567. *  get single character and discard remainder of input line
  568. */
  569. char
  570. getc_line( c )
  571. char *c;
  572. {
  573.     scanf( "%c", c );    /* c is the address ( pointer ) for the char storage */
  574.     eat_line( *c );
  575.     return( *c );
  576. } /* getc_line */
  577.  
  578. /*
  579. *  discard current input line
  580. */
  581. void eat_line( char c )
  582. {
  583.     while ( c != '\n' ) c = getchar( );    /* eat the remaining chars on the line */
  584. } /* eat_line */
  585.  
  586. /*
  587. *  simple way to coordinate some timing tasks...
  588. */
  589. void
  590. press_enter_key( message )
  591. char *message;
  592. {    int c;
  593.     printf( "%s", message );
  594.     eat_line( c );
  595. } /* press_enter_key */
  596.  
  597.  
  598. /*
  599. *  get string within length limit allow only single white spaces
  600. */
  601. char *
  602. gets_lim_ows( max_len, s )
  603. int max_len;
  604. char *s;
  605. {
  606.     char c;
  607.     int i = 0;
  608.     int last_space = TRUE;
  609.  
  610.     while ( ( ( c = getchar( ) ) != '\n' ) && ( i < ( max_len - 1 ) ) )
  611.     {
  612.         if ( isspace( c ) )
  613.         {
  614.             c = ' ';    /* ensure that c is a char space - not other white space */
  615.             if ( !( last_space ) )
  616.             {            /* last char was not a space    */
  617.                 s[i++] = c;    /* so add to string        */
  618.                 /*printf( "\nsave char [%c]", c );    sls-debug */
  619.             }
  620.             last_space = TRUE;    /* mark last char read was a white space */
  621.         }
  622.         else
  623.         {
  624.             s[i++] = c;
  625.             last_space = FALSE;    /* mark last char read was not a space */
  626.             /*printf( "\nsave char [%c]", c );    sls-debug */
  627.         }
  628.     }
  629.  
  630.     /* don't permit to end on space char - so move backward to first non-white space char         */
  631.     /* note that this may result in one-less than the maximum number of chars available being read     */
  632.     while ( isspace( s[--i] ) ) { /* do nothing... */ }
  633.  
  634.     s[++i] = NULL;
  635.  
  636.     eat_line( c );    /* eat the remaining characters on this line */
  637.  
  638.     return( s );
  639. } /* gets_lim_ows */
  640.  
  641.